﻿using Google.Protobuf.WellKnownTypes;
using IdentityModel;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ApplicationParts;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration.Json;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders;
using Microsoft.IdentityModel.Tokens;
using Swashbuckle.AspNetCore.Filters;
using Swashbuckle.AspNetCore.SwaggerGen;
using Swashbuckle.AspNetCore.SwaggerUI;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Security.Claims;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using ZhCun.WebUtils.Configs;

namespace ZhCun.WebUtils
{
    /// <summary>
    /// NETCore 3.0 的自定义配置
    /// </summary>
    public static class GlobalServiceConfig
    {
        public static IConfigurationRoot ConfigRoot { get; private set; }

        public static void InitConfigFile()
        {
            var builder = new ConfigurationBuilder();
            
            builder.Add(new JsonConfigurationSource { Path = "appsettings.json", ReloadOnChange = true });
            ConfigRoot = builder.Build();
            ConfigRoot.Bind("JwtConfig", JwtConfig.THIS);
            ConfigRoot.Bind("CommonConfig", CommonConfig.THIS);
        }

        public static IServiceCollection InitCustomService(this IServiceCollection services, Action<IConfigurationRoot> ConfigHandle = null)
        {
            InitConfigFile();

            ConfigHandle?.Invoke(ConfigRoot);

            services.InitJwtBearer();

            services                
                .AddControllersWithViews((options) =>
                {
                    options.Filters.Add<GlobalExceptionsFilter>();
                    
                })
                .ConfigureApplicationPartManager((manager) =>
                {
                    var assembly = typeof(GlobalServiceConfig).Assembly;
                    manager.ApplicationParts.Add(new AssemblyPart(assembly));

                    //manager.FeatureProviders.Add(new ControllerFeatureProvider());
                    //var feature = new ControllerFeature();
                    //manager.PopulateFeature(feature);
                    //services.AddSingleton(feature.Controllers.Select(t => t.AsType()).ToArray());

                })

                //.AddNewtonsoftJson((opt)=> 
                //{
                //    使用中文编码问题没有问题
                //    opt.SerializerSettings.ContractResolver = new Newtonsoft.Json.Serialization.DefaultContractResolver();//json字符串原样输出
                //})
                //.AddMvc()
                .InitJsonOptions();

            return services;
        }

        public static IMvcBuilder InitJsonOptions(this IMvcBuilder builder, Action<JsonOptions> setupAction = null)
        {
            return
            builder
                .AddJsonOptions((options) =>
                 {
                     JsonHelper.SetJsonOption(options);                     
                 });
        }

        public static void InitJwtBearer(this IServiceCollection services)
        {
            //参考: https://www.cnblogs.com/diwu0510/p/11562545.html
            //IdentityModel
            //Microsoft.AspNetCore.Authorization.JwtBearer
            if (!JwtConfig.THIS.IsEnabled) return;

            services
                .AddAuthentication(options =>
                {
                    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
                })
                .AddJwtBearer(options =>
                {
                    options.Audience = JwtConfig.THIS.Audience;

                    options.TokenValidationParameters = new TokenValidationParameters
                    {
                        // The signing key must match!
                        ValidateIssuerSigningKey = true,
                        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(JwtConfig.THIS.SecretKey)),

                        // Validate the JWT Issuer (iss) claim
                        ValidateIssuer = true,
                        ValidIssuer = JwtConfig.THIS.Issuer,

                        // Validate the JWT Audience (aud) claim
                        ValidateAudience = true,
                        ValidAudience = JwtConfig.THIS.Audience,

                        // Validate the token expiry
                        ValidateLifetime = true,

                        // If you want to allow a certain amount of clock drift, set that here
                        ClockSkew = TimeSpan.Zero
                    };
                });
        }

        /// <summary>
        /// 初始化Swagger
        /// </summary>
        public static IServiceCollection InitSwagger(this IServiceCollection services, Action<SwaggerGenOptions> setupAction)
        {
            if (!CommonConfig.THIS.SwaggerEnable)
            {
                return services;
            }

            return
            services.AddSwaggerGen(c =>
            {
                setupAction?.Invoke(c);

                c.OperationFilter<SwaggerOperationFilter>();

                c.DocumentFilter<SwaggerHiddenFilter>();

                c.OperationFilter<AddResponseHeadersFilter>();

                c.OperationFilter<AppendAuthorizeToSummaryOperationFilter>();

                c.OperationFilter<SecurityRequirementsOperationFilter>();

                //c.AddSecurityDefinition(CommonConfig.THIS.TokenName, new Microsoft.OpenApi.Models.OpenApiSecurityScheme
                //{
                //    //Name = "Authorization",
                //    Name = CommonConfig.THIS.TokenName,
                //    In = Microsoft.OpenApi.Models.ParameterLocation.Header,                    
                //    //Type = Microsoft.OpenApi.Models.SecuritySchemeType.OpenIdConnect,
                //    //Description = "JWT 授权，格式：Bearer+空格+{token}"
                //    Description = "Token值"
                //});

                // 为 Swagger JSON and UI设置xml文档注释路径                
                //获取当前程序的
                var fileName = Assembly.GetEntryAssembly().Location;
                var path = Path.GetDirectoryName(fileName);
                var xmlFiles = CommonConfig.THIS.SwaggerXmlComments.Split(',');
                foreach (var xmlFile in xmlFiles)
                {
                    c.IncludeXmlComments(Path.Combine(path, xmlFile));
                }
            });
        }

        public static IApplicationBuilder InitAppBuiler(this IApplicationBuilder app)
        {
            app.UseMiddleware<GlobalExceptionMiddleware>();

            //认证中间件必须放在使用MVC中间件之前
            //app.UseAuthentication();

            app.UseStaticFiles();

            //#if DEBUG
            //            app.UseStaticFiles();
            //#else
            //            app.UseStaticFiles(Path.Combine(AppContext.BaseDirectory, @"wwwroot"));
            //#endif
            // 设置允许所有来源跨域
            app.UseCors(options =>
            {
                options.AllowAnyHeader();
                //options.AllowAnyMethod();
                //options.AllowAnyOrigin();
                //options.AllowCredentials();
            });

            return app;
        }

        /// <summary>
        /// 初始化swaggeer app
        /// </summary>
        public static IApplicationBuilder InitSwaggerApp(this IApplicationBuilder app, Action<SwaggerUIOptions> setupAction = null)
        {
            if (!CommonConfig.THIS.SwaggerEnable) return app;

            var prefix = CommonConfig.THIS.SwaggerRoutePrefix;
            //app.UseMvc(routes =>
            //{
            //    routes.MapRoute(
            //        name: "swagger",
            //        template: "/swagger");
            //});

            //启用中间件服务生成Swagger作为JSON终结点
            app.UseSwagger();
            //启用中间件服务对swagger-ui，指定Swagger JSON终结点
            app.UseSwaggerUI(c =>
            {
                setupAction?.Invoke(c);
                //c.SwaggerEndpoint("/swagger/v1/swagger.json", "Test API");
                //c.SwaggerEndpoint("/swagger/v2/swagger.json", "用户 API");
                //c.SwaggerEndpoint("/swagger/v3/swagger.json", "业务 API");
                c.RoutePrefix = $"{prefix}"; //如果在主页显示，则改为：Empty，区分大小写
            });
            return app;
        }
    }
}